package org.sqlproc.engine.impl;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Map;

/**
 * SQL Processor utilities.
 * 
 * @author <a href="mailto:Vladimir.Hudec@gmail.com">Vladimir Hudec</a>
 */
public class SqlUtils {

    static final String GET_PREFIX_BOOLEAN = "is";
    static final int GET_PREFIX_BOOLEAN_LEN = GET_PREFIX_BOOLEAN.length();
    static final String GET_PREFIX_COMMON = "get";
    static final int GET_PREFIX_COMMON_LEN = GET_PREFIX_COMMON.length();
    static final String SET_PREFIX_COMMON = "set";
    static final int SET_PREFIX_COMMON_LEN = SET_PREFIX_COMMON.length();

    private SqlUtils() {
    }

    static void appendJavaIdent(StringBuilder s, String ident) {
        int len = (ident != null) ? ident.length() : 0;
        if (s != null && len > 0) {
            s.append(Character.toUpperCase(ident.charAt(0)));
            if (len > 1)
                s.append(ident.substring(1));
        }
    }

    static String is(String name) {
        int len = (name != null) ? name.length() : 0;
        return is(name, len);
    }

    static String get(String name) {
        int len = (name != null) ? name.length() : 0;
        return get(name, len);
    }

    static String set(String name) {
        int len = (name != null) ? name.length() : 0;
        return set(name, len);
    }

    static String is(String name, int len) {
        StringBuilder s = new StringBuilder(len + GET_PREFIX_BOOLEAN_LEN);
        s.append(GET_PREFIX_BOOLEAN);
        appendJavaIdent(s, name);
        return s.toString();
    }

    static String get(String name, int len) {
        StringBuilder s = new StringBuilder(len + GET_PREFIX_COMMON_LEN);
        s.append(GET_PREFIX_COMMON);
        appendJavaIdent(s, name);
        return s.toString();
    }

    static String set(String name, int len) {
        StringBuilder s = new StringBuilder(len + SET_PREFIX_COMMON_LEN);
        s.append(SET_PREFIX_COMMON);
        appendJavaIdent(s, name);
        return s.toString();
    }

    // Reflection

    static Method getMethod(Class<?> clazz, String... names) {
        for (String name : names) {
            try {
                Method m = clazz.getMethod(name, (Class[]) null);
                if (Modifier.isPublic(m.getModifiers()))
                    return m;
            } catch (NoSuchMethodException ex) {
            } catch (SecurityException ex) {
            }
        }
        return null;
    }

    static Method getMethodIgnoreParameters(Class<?> clazz, String name) {
        Method m = null;
        try {
            Method[] mm = clazz.getMethods();
            if (mm != null) {
                for (int i = 0; i < mm.length; i++) {
                    if (name.equals(mm[i].getName()) && Modifier.isPublic(mm[i].getModifiers())) {
                        m = mm[i];
                        break;
                    }
                }
            }
        } catch (SecurityException ex) {
            m = null;
        }
        return m;
    }

    static Method getMethod(Class<?> clazz, Class<?> param, String... names) {
        for (String name : names) {
            try {
                Method m = clazz.getMethod(name, new Class[] { param });
                if (Modifier.isPublic(m.getModifiers()))
                    return m;
            } catch (NoSuchMethodException ex) {
            } catch (SecurityException ex) {
            }
        }
        return null;
    }

    static Method getMethod(Class<?> clazz, String name, Class<?> param) {
        return getMethod(clazz, param, new String[] { name });
    }

    static Method getSetter(Object pojo, String attributeName, Class<?>... classes) {
        for (Class<?> clazz : classes) {
            Method m = SqlUtils.getMethod(pojo.getClass(), SqlUtils.set(attributeName), clazz);
            if (m != null)
                return m;
        }
        return null;
    }

    static Method getMethodEnumToValue(Class<?> clazz) {
        return getMethod(clazz, "getCode", "getValue", "getName");
    }

    static Method getMethodValueToEnum(Class<?> clazz, Class<?> param) {
        return getMethod(clazz, param, "fromCode", "fromValue", "valueOf");
    }

    static Class<?> getFieldType(Class<?> clazz, String name) {
        try {
            Field f = clazz.getDeclaredField(name);
            return f.getType();
        } catch (SecurityException e) {
            throw new RuntimeException(e);
        } catch (NoSuchFieldException e) {
            throw new RuntimeException(e);
        }
    }

    // invocations

    static Object invokeMethod(Object obj, String nameGet, String nameIs) {
        Method m = SqlUtils.getMethod(obj.getClass(), nameGet, nameIs);
        obj = SqlUtils.invokeMethod(m, obj);
        return obj;
    }

    static Object invokeMethod(Method m, Object obj) {
        Object result;
        if (m != null) {
            try {
                result = m.invoke(obj);
            } catch (IllegalAccessException e) {
                throw new RuntimeException(e);
            } catch (IllegalArgumentException e) {
                throw new RuntimeException(e);
            } catch (InvocationTargetException e) {
                throw new RuntimeException(e);
            }
        } else
            result = null;
        return result;
    }

    static Object invokeMethod(Method m, Object obj, Object param) {
        Object result;
        if (m != null) {
            try {
                result = m.invoke(obj, param);
            } catch (IllegalAccessException e) {
                throw new RuntimeException(e);
            } catch (IllegalArgumentException e) {
                throw new RuntimeException(e);
            } catch (InvocationTargetException e) {
                throw new RuntimeException(e);
            }
        } else
            result = null;
        return result;
    }

    public static <E> Object getInstance(Class<E> resultClass) {
        E instance = null;
        try {
            Constructor<E> constructor = resultClass.getConstructor();
            instance = constructor.newInstance();
            return instance;
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        } catch (SecurityException e) {
            throw new RuntimeException(e);
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        } catch (IllegalArgumentException e) {
            throw new RuntimeException(e);
        } catch (InvocationTargetException e) {
            throw new RuntimeException(e);
        }
    }

    // miscs

    static Byte[] toBytes(byte[] barr) {
        if (barr == null)
            return null;
        Byte[] res = new Byte[barr.length];
        for (int i = 0; i < barr.length; i++)
            res[i] = new Byte(barr[i]);
        return res;
    }

    static byte[] toBytes(Byte[] barr) {
        if (barr == null)
            return null;
        byte[] res = new byte[barr.length];
        for (int i = 0; i < barr.length; i++)
            res[i] = (barr[i] != null) ? barr[i].byteValue() : (byte) 0;
        return res;
    }

    public static String getFeature(Map<String, Object> features, String name) {
        Object o = features.get(name);
        return (o != null && o instanceof String) ? (String) o : null;
    }

    public static Boolean isFeature(Map<String, Object> features, String name, Boolean defaultValue) {
        Object o = features.get(name);
        if (o == null || !(o instanceof Boolean))
            return defaultValue;
        return (Boolean) o;
    }
}
